﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Threading;
using System.Diagnostics;
using Bohdan.Arch.Messaging;

namespace Bohdan.Arch.Messaging.Special
{
    public class TimeCheckedMessage : GeneralMessage
    {
        protected GeneralMessage _primaryMessage;
        protected TimeSpan _estimatedTimeSpan;
        protected GeneralMessage _longExecutionReportingMessage;
        protected GeneralMessage _longExecutionFallbackMessage;
        protected Stopwatch _stopwatch;
        protected volatile bool _timeExceeded = false;
        //protected object _syncObj = new object();

        protected EventWaitHandle _ewhMessageProcessed = new EventWaitHandle(false, EventResetMode.ManualReset);

        public GeneralMessage PrimaryMessage
        {
            get { return _primaryMessage; }
        }
        public TimeSpan EstimatedTimeSpan
        {
            get { return _estimatedTimeSpan; }
        }
        public GeneralMessage LongExecutionReportingMessage
        {
            get { return _longExecutionReportingMessage; }
        }
        public GeneralMessage LongExecutionFallbackMessage
        {
            get { return _longExecutionFallbackMessage; }
        }

        public TimeCheckedMessage(GeneralMessage primaryMessage,
                                  TimeSpan estimatedTimeSpan,
                                  GeneralMessage longExecutionReportingMessage,
                                  GeneralMessage longExecutionFallbackMessage)
        {
            this._primaryMessage = primaryMessage;
            this._estimatedTimeSpan = estimatedTimeSpan;
            this._longExecutionReportingMessage = longExecutionReportingMessage;
            this._longExecutionFallbackMessage = longExecutionFallbackMessage;

            primaryMessage.OnMessageProcessed += new EventHandler(primaryMessage_OnMessageProcessed);
            _stopwatch = new Stopwatch();
        }

        public override Guid TargetSubscriberId
        {
            get { return TimeMeasurerSubscriber._Id; }
        }

        private void CheckThread()
        {
            if (!_ewhMessageProcessed.WaitOne(_estimatedTimeSpan))
            {
                lock (_syncObj)
                {
                    if (!_timeExceeded)
                    {
                        _timeExceeded = true;
                        Bus.PostMessageAsync(_longExecutionReportingMessage);
                    }
                }
            }
        }

        public void StartCheckAsync(MessagingBus _bus)
        {
            this.Bus = _bus;
            _stopwatch.Start();
            Thread worker = new Thread(new ThreadStart(CheckThread));
            worker.Priority = ThreadPriority.Highest;
            worker.Start();
        }

        void primaryMessage_OnMessageProcessed(object sender, EventArgs e)
        {
            _ewhMessageProcessed.Set();
            _stopwatch.Stop();
#if TRACE_MESSAGE_BUS
            Trace.TraceInformation("Message {0} has been processing (after queue) for {1} ms.", _primaryMessage.ToString(), _stopwatch.ElapsedMilliseconds);
#endif
            lock (_syncObj)
            {
                if (_timeExceeded)
                {
                    Bus.PostMessageAsync(_longExecutionFallbackMessage);
                }
                _timeExceeded = true; // flag to protect from posting _longExecutionReportingMessage
            }
        }

        public override bool WaitCompleted()
        {
            base.WaitCompleted();
            PrimaryMessage.WaitCompleted();

            bool waitLongExecutionMessages = false;
            lock (_syncObj)
            {
                waitLongExecutionMessages = _timeExceeded;
            }

            if (waitLongExecutionMessages)
            {
                LongExecutionReportingMessage.WaitCompleted();
                LongExecutionFallbackMessage.WaitCompleted();
            }

            return true;
        }

        public override bool WaitCompleted(TimeSpan ts)
        {
            Stopwatch sw = null;

            sw = Stopwatch.StartNew();
            if (!base.WaitCompleted(ts))
                return false;
            sw.Stop();
            ts = ts.Subtract(sw.Elapsed);

            sw = Stopwatch.StartNew();
            if (!PrimaryMessage.WaitCompleted(ts))
                return false;
            sw.Stop();
            ts = ts.Subtract(sw.Elapsed);

            bool waitLongExecutionMessages = false;
            lock (_syncObj)
            {
                waitLongExecutionMessages = _timeExceeded;
            }

            if (waitLongExecutionMessages)
            {
                sw = Stopwatch.StartNew();
                if (!LongExecutionReportingMessage.WaitCompleted(ts))
                    return false;
                sw.Stop();
                ts = ts.Subtract(sw.Elapsed);
                
                if (!LongExecutionFallbackMessage.WaitCompleted(ts))
                    return false;
            }

            return true;
        }
    }
}
